import { defineStore } from 'pinia'
import {
	BetslipTab,
	BetslipItemType,
	BetslipInfoType,
	BetslipItemStatus,
	BetslipComboType,
	BetslipBaseType,
	BettingOrderType
} from '@/types'
import { BettingMethod, BettingOrderStatus, AcceptOddsMode } from '@/enums'
import { getIndexById, removeById, removeByItem, getItemById, getComboOdds, sleep } from '@/utils'
import {
	useAppStore,
	useBettingConfigStore,
	useMatchStore,
	useWalletStore,
	useLayoutStore,
	useFeedStore
} from '@/store'
import { placeBets, prepareBets } from '@/api/betting'
import { HTTP_CODES } from '@/constants'
import { isEmpty } from 'lodash-es'

export const useBetslipStore = defineStore('betslip', {
	state: () => ({
		tab: BetslipTab.Single as BetslipTab,
		list: [] as BetslipItemType[],
		acceptOddsMode: AcceptOddsMode.AcceptAny,
		combo: {
			amount: 0,
			type: BettingMethod.combo,
			status: BetslipItemStatus.none
		} as BetslipComboType,
		successList: [] as BetslipBaseType[],
		followList: [] as BetslipBaseType[],
		fromRect: null as DOMRect | null
	}),
	actions: {
		clear() {
			if (this.tab !== BetslipTab.Follow) {
				this.list = []
			} else {
				this.followList = []
			}
		},
		changeTab(val: BetslipTab) {
			this.tab = val
			if (val === BetslipTab.Single) {
				this.list.forEach((item) => {
					this.autoFillAmount(item)
					this.prepareBets(item)
				})
			} else if (val === BetslipTab.Combo) {
				this.autoFillAmount(this.combo)
				this.prepareBets(this.combo, this.list)
			}
			this.successList = []
			this.combo.status = BetslipItemStatus.none
		},
		async select(item: BetslipItemType, rect: DOMRect | null = null) {
			const layoutStore = useLayoutStore()
			if (layoutStore.sidebar !== 'betslip') {
				layoutStore.setSidebar('betslip')
				await sleep(150)
			}
			if (this.tab === BetslipTab.Follow) {
				this.tab = BetslipTab.Single
				await sleep(0)
			}
			if (
				this.list.some((item) => item.status === BetslipItemStatus.confirming) ||
				this.combo.status === BetslipItemStatus.confirming
			) {
				return
			}
			this.fromRect = rect
			const index = getIndexById(this.list, item.id)
			if (index > -1) {
				this.list.splice(index, 1)
			} else {
				removeById(this.list, item.marketId, 'marketId')
				this.list.push(item)
			}
			if (this.tab === BetslipTab.Single && index === -1) {
				this.autoFillAmount(item)
				this.prepareBets(item)
			} else if (this.tab === BetslipTab.Combo) {
				this.prepareBets(this.combo, this.list)
			}
			this.successList = []
			this.combo.status = BetslipItemStatus.none
		},
		async follow(item: BetslipBaseType, rect: DOMRect | null = null) {
			const layoutStore = useLayoutStore()
			if (layoutStore.sidebar !== 'betslip') {
				layoutStore.setSidebar('betslip')
				await sleep(150)
			}
			if (this.tab !== BetslipTab.Follow) {
				this.tab = BetslipTab.Follow
				await sleep(0)
			}
			if (this.followList.some((item) => item.status === BetslipItemStatus.confirming)) {
				return
			}
			this.fromRect = rect
			const index = this.followList.indexOf(item)
			if (index > -1) {
				this.followList.splice(index, 1)
				return
			}
			this.followList.push(item)
			if (item.type === BettingMethod.single) {
				this.prepareBets(item)
			} else {
				this.prepareBets(item, (item as BetslipComboType).list)
			}
			this.autoFillAmount(item)
			this.successList = []
			this.combo.status = BetslipItemStatus.none
		},
		setAmount(item: BetslipBaseType, val: number) {
			item.amount = val
		},
		setStatus(id: string, val: BetslipItemStatus) {
			const item = getItemById<BetslipItemType>(this.list, id)
			if (item) item.status = val
		},
		setComboInfo(val: Partial<BetslipComboType>) {
			this.combo = { ...this.combo, ...val }
		},
		autoFillAmount(item: BetslipBaseType) {
			const bettingConfigStore = useBettingConfigStore()
			{
				if (!item.amount) {
					const lastInput =
						item.type === BettingMethod.single
							? bettingConfigStore.lastInputSingle
							: bettingConfigStore.lastInputCombo
					if (lastInput) {
						item.amount = lastInput
					} else if (bettingConfigStore.quickInputList.length) {
						item.amount = bettingConfigStore.quickInputList[0]
					} else {
						item.amount = 0
					}
				}
			}
		},
		addSuccess(item: BetslipBaseType) {
			this.successList.unshift(item)
			if (this.tab === BetslipTab.Single) {
				removeById(this.list, (item as BetslipItemType).id)
			} else if (this.tab === BetslipTab.Combo) {
				this.list = []
				this.combo.status = BetslipItemStatus.none
			} else if (this.tab === BetslipTab.Follow) {
				removeByItem(this.followList, item)
			}
		},
		removeSuccess(orderNo?: string) {
			if (!orderNo) {
				this.successList = []
			} else {
				this.successList = this.successList.filter(({ order }) => order?.orderNo !== orderNo)
			}
		},
		updateStatus(item: BetslipBaseType, order: BettingOrderType) {
			const feedStore = useFeedStore()
			const bettingConfigStore = useBettingConfigStore()
			item.order = order
			if (order.status === BettingOrderStatus.successed) {
				const walletStore = useWalletStore()
				walletStore.getBalance()
				item.status = BetslipItemStatus.success
				if (item.feedId) {
					feedStore.addFollow(item.feedId)
				}
				bettingConfigStore.setLastInput(item.type, item.amount)
			} else if (order.status === BettingOrderStatus.failed) {
				item.status = BetslipItemStatus.fail
			}
		},
		submitBets() {
			if (this.tab === BetslipTab.Single) {
				this.list.forEach((item) => this.placeBets(item))
			} else if (this.tab === BetslipTab.Combo) {
				this.combo.list = this.list.map(({ id, eventId, marketId }) => ({ id, eventId, marketId }))
				this.placeBets(this.combo, this.list)
			} else if (this.tab === BetslipTab.Follow) {
				this.followList.forEach((item) => {
					if (item.type === BettingMethod.single) {
						this.placeBets(item)
					} else {
						this.placeBets(item, (item as BetslipComboType).list)
					}
				})
			}
		},
		async prepareBets(item: BetslipBaseType, list: BetslipInfoType[] = []) {
			const type = item.type
			try {
				const appStore = useAppStore()
				const bettingConfigStore = useBettingConfigStore()
				if (type === BettingMethod.combo && list.length < 2) {
					;(item as BetslipComboType).disableIds = []
					return
				}
				const { data } = await prepareBets({
					type,
					locale: appStore.lang,
					oddsStyle: appStore.odds,
					items: (type === BettingMethod.single ? [item] : list).map((item) => ({
						selectionId: (item as BetslipItemType).id,
						eventId: (item as BetslipItemType).eventId
					}))
				})
				const { trueOdds, displayOdds, minStake, maxStake, freeBets, comboBonus } = data.bet
				let minAmount = Math.max(minStake, bettingConfigStore.bettingMin)
				let maxAmount = Math.min(maxStake, bettingConfigStore.bettingMax)
				if (+displayOdds < 0) {
					minAmount /= -displayOdds
					maxAmount /= -displayOdds
				}
				item.trueOdds = trueOdds
				item.displayOdds = displayOdds
				item.freeBets = freeBets
				item.minStake = minAmount
				item.maxStake = maxAmount
				if (item.minStake && item.amount < item.minStake) {
					item.amount = item.minStake
				} else if (item.maxStake && item.amount > item.maxStake) {
					item.amount = item.maxStake
				}
				if (item.status !== BetslipItemStatus.confirming) {
					item.status = BetslipItemStatus.none
				}
				if (type === BettingMethod.combo) {
					;(item as BetslipComboType).comboBonus = comboBonus
					;(item as BetslipComboType).disableIds = []
				}
			} catch (error: any) {
				const { data } = error
				if (!data || !data.nonActiveItems || !data.nonComboItems || !data.disabledItems) {
					return
				}
				const { nonActiveItems, disabledItems, nonComboItems } = data
				;[...nonActiveItems, ...disabledItems].forEach((id) =>
					this.setStatus(id, BetslipItemStatus.disabled)
				)
				if (type === BettingMethod.combo) {
					;(item as BetslipComboType).disableIds = nonComboItems
				}
			}
		},
		async placeBets(item: BetslipBaseType, list: BetslipInfoType[] = []) {
			const type = item.type
			const appStore = useAppStore()
			const matchStore = useMatchStore()
			item.status = BetslipItemStatus.confirming
			item.order = null
			await this.prepareBets(item, list)
			let resData: any
			try {
				const { freeBet, followOrder } = item
				const betItem: any = {
					type,
					numberOfBets: 1,
					followOrder
				}
				if (type === BettingMethod.single) {
					const {
						eventId,
						marketId,
						id,
						amount,
						displayOdds,
						stake: negStake
					} = item as BetslipItemType
					const selection = matchStore.getSelection(eventId, marketId, id) || item.selection
					let stake = amount
					if (displayOdds && +displayOdds < 0) {
						stake = negStake || +amount * -displayOdds
					}
					Object.assign(betItem, {
						stake: stake,
						trueOdds: item.trueOdds,
						displayOdds: item.displayOdds,
						items: [
							{
								...selection,
								eventId: eventId
							}
						]
					})
				} else {
					const { trueOdds, displayOdds, amount, selections, comboBonus } = item as BetslipComboType
					Object.assign(betItem, {
						stake: amount,
						comboBonus: !isEmpty(comboBonus) ? comboBonus : undefined,
						trueOdds,
						displayOdds,
						items: list.map((item, index) => {
							let selection: any = matchStore.getSelection(item.eventId, item.marketId, item.id)
							if (!selection && selections) {
								selection = selections[index]
							}
							return {
								...selection,
								eventId: item.eventId
							}
						})
					})
				}
				if (freeBet) {
					// betItem.bonus = {
					// 	id: freeBet.assignId,
					// 	isRiskFree: freeBet.isRiskFree
					// }
				}
				item.order = null
				resData = await placeBets({
					locale: appStore.lang,
					oddsStyle: appStore.odds,
					acceptMode: this.acceptOddsMode,
					bets: [betItem]
				})
			} catch (error) {
				resData = error
			} finally {
				const { code, data } = resData
				if (code === HTTP_CODES.Success || code === HTTP_CODES.BetOrderProcessing) {
					const orderNos: BettingOrderType[] = data.orderNos
					if (orderNos.length) {
						const order: BettingOrderType = orderNos[0]
						this.updateStatus(item, order)
					}
				} else {
					item.status = BetslipItemStatus.fail
				}
			}
		}
	},
	getters: {
		betAmountSum: (state) => {
			if (state.tab === BetslipTab.Single) {
				return state.list.reduce((prev, item) => prev + item.amount, 0)
			} else if (state.tab === BetslipTab.Combo) {
				return state.combo.amount
			} else if (state.tab === BetslipTab.Follow) {
				return state.followList.reduce((prev, item) => prev + item.amount, 0)
			}
		},
		awardAmountSum(state) {
			const matchStore = useMatchStore()
			const bettingConfigStore = useBettingConfigStore()
			if (state.tab === BetslipTab.Single) {
				return state.list.reduce((prev, item) => {
					const selection = matchStore.getSelection(item.eventId, item.marketId, item.id)
					return prev + item.amount * (selection?.trueOdds || 0)
				}, 0)
			} else if (state.tab === BetslipTab.Combo) {
				const odds = state.list.map(
					(item) => matchStore.getSelection(item.eventId, item.marketId, item.id)?.trueOdds || 1
				)
				const comboOdds = getComboOdds(
					odds,
					bettingConfigStore.comboBonus,
					bettingConfigStore.comboBonusMinTrueOdds
				)
				return state.combo.amount * comboOdds
			} else if (state.tab === BetslipTab.Follow) {
				return state.followList.reduce(
					(prev, item) => prev + (item.trueOdds ? item.amount * item.trueOdds : 0),
					0
				)
			}
		},
		isInBetslip: (state) => (selectionId: string) =>
			state.list.find(({ id }) => id === selectionId),
		comboList: (state) =>
			state.list.filter((item) => state.combo?.disableIds?.indexOf(item.id) === -1),
		comboDisabledList: (state) =>
			state.list.filter((item) => state.combo?.disableIds?.indexOf(item.id) !== -1)
	},
	persist: true
})
